using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using ALib;
using ALib.Data.IO;
using ALib.Misc;

namespace TradingLab.Data
{
    internal class TimeComparer : IComparer<ObjectTuple2<TimeSpan, TimeData>>
    {
        public int Compare(ObjectTuple2<TimeSpan, TimeData> x, ObjectTuple2<TimeSpan, TimeData> y)
        {
            int result;

            if (x.Object0 == y.Object0)
            {
                result = 0;
            }
            else if (x.Object0 < y.Object0)
            {
                result = -1;
            }
            else
            {
                result = 1;
            }
            return result;
        }
    }

    [Serializable]
    // <ticker name, ticker data>
    public class XDateData : List<ObjectTuple2<TimeSpan, TimeData>>
    {
        private int _currentTimeDataIndex;

        public int CurrentTimeDataIndex
        {
            get { return _currentTimeDataIndex; }
            set
            {
                GoodValue.Assert(value < Count);
                _currentTimeDataIndex = value;
            }
        }

        public bool IsEndOfTimeData
        {
            get
            {
                bool result = _currentTimeDataIndex >= Count;
                return result;
            }
        }

        public XDateData()
        {
            _currentTimeDataIndex = 0;
        }

        public void SortByTime()
        {
            Sort(new TimeComparer());
        }

        public bool Contains(TimeSpan time)
        {
            bool result = false;
            foreach (ObjectTuple2<TimeSpan, TimeData> time_TimeData in this)
            {
                if (time == time_TimeData.Object0)
                {
                    result = true;
                    break;
                }
            }
            return result;
        }

        public TimeSpan StartTime
        {
            get
            {
                TimeSpan result = new TimeSpan();
                if (Count>0)
                {
                    result = this[0].Object0;
                }
                return result;
            }
        }

        public TimeSpan EndTime
        {
            get
            {
                TimeSpan result = new TimeSpan();
                if (Count > 0)
                {
                    result = this[Count - 1].Object0;
                }
                return result;
            }
        }

        public void MergeByTime()
        {
            Dictionary<TimeSpan, TimeData> result = new Dictionary<TimeSpan, TimeData>();

            foreach (ObjectTuple2<TimeSpan, TimeData> time_TimeData in this)
            {
                if (!result.ContainsKey(time_TimeData.Object0))
                {
                    result.Add(time_TimeData.Object0, time_TimeData.Object1);
                }
                else
                {
                    result[time_TimeData.Object0].MergeWith(time_TimeData.Object1);
                }
            }

            Clear();
            foreach (KeyValuePair<TimeSpan, TimeData> time_TimeData in result)
            {
                Add(new ObjectTuple2<TimeSpan, TimeData>(time_TimeData.Key, time_TimeData.Value));
            }
        }

        public void MergeAndSortByTime()
        {
            MergeByTime();
            SortByTime();
        }

        public void SaveToPlainFiles(DirectoryInfo outputDir)
        {
            foreach (ObjectTuple2<TimeSpan, TimeData> time_TimeData in this)
            {
                string timeString = (new DateTime() + time_TimeData.Object0).ToString(DateTimeFormatType.TimeSimple);
                foreach (KeyValuePair<string, TickerData> tickerName_TickerData in time_TimeData.Object1)
                {
                    string tickerName = tickerName_TickerData.Key;
                    TickerData tickerData = tickerName_TickerData.Value;
                    foreach (KeyValuePair<string, PropertyBoxData> propertyBoxName_PropertyBoxData in tickerData)
                    {
                        string propertyBoxName = propertyBoxName_PropertyBoxData.Key;
                        PropertyBoxData propertyBoxData = propertyBoxName_PropertyBoxData.Value;
                        List<string> output = new List<string>();
                        foreach (KeyValuePair<string, PropertyData> propertyName_PropertyData in propertyBoxData)
                        {
                            string propertyName = propertyName_PropertyData.Key;
                            output.Add("<" + propertyName + ">");
                            foreach (string propertyDataRecord in propertyName_PropertyData.Value)
                            {
                                output.Add(propertyDataRecord);
                            }
                        }
                        string fileName = outputDir.FullName
                                          + @"\" + timeString
                                          + @"\" + tickerName
                                          + @"\" + propertyBoxName + ".csv";
                        FilesIO.ToOneFile(output, new FileInfo(fileName));
                    }
                }
            }
        }

        public void Serialize(FileInfo outputFile)
        {
            ObjectIO.SerializeObject(this, outputFile.FullName, true);
        }

        public static XDateData Deserialize(FileInfo inputFile)
        {
            XDateData result = ObjectIO.DeserializeObject(inputFile.FullName, true) as XDateData;
            return result;
        }

        public void Add(TimeSpan time, string tickerName, string propertyBoxName, string propertyName, string propertyValue)
        {
            TimeData timeData = new TimeData();
            timeData.Add(tickerName, propertyBoxName, propertyName, propertyValue);
            Add(new ObjectTuple2<TimeSpan, TimeData>(time, timeData));
        }

        public ObjectTuple2<TimeSpan, TimeData> GetNextTimeData()
        {
            ObjectTuple2<TimeSpan, TimeData> result = null;

            if (!IsEndOfTimeData)
            {
                result = this[_currentTimeDataIndex];
                _currentTimeDataIndex++;
            }
            return result;
        }

        public void ResetTimeDataIndex()
        {
            _currentTimeDataIndex = 0;
        }
    }
}